/*
 * Calendario
 * v1.1.08
 * Arcadio Carballares Martin, 2012
 * http://www.arcadiocarballares.com 
 */
package es.arcadio {
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	import mx.collections.ArrayCollection;
	import mx.collections.ArrayList;
	import mx.controls.Button;
	import mx.events.ListEvent;
	import mx.events.NumericStepperEvent;
	
	import spark.components.ComboBox;
	import spark.components.Label;
	import spark.components.NumericStepper;
	import spark.components.SkinnableContainer;
	import spark.events.IndexChangeEvent;
	import spark.layouts.HorizontalLayout;
	import spark.layouts.TileLayout;
	import spark.layouts.VerticalLayout;
	
	[Event(name="select", type="es.arcadio.SelectDayEvent")]
	[Event(name="deselect", type="es.arcadio.SelectDayEvent")]
	[Event(name="change", type="es.arcadio.SelectDayEvent")]
	
	[Bindable]
	public class Calendario extends SkinnableContainer {
		//private const WEEK:int = 6;
		
		public var dayNames:Array=new Array('Su', 'Mo','Tu','We','Th','Fr','Sa');
		public var monthNames:Array=new Array('January', 'February', 'March', 'April', 'May',
      		'June', 'July', 'August', 'September', 'October', 'November',
      		'December');
      	public var rowCount:int=12;
      	public var firstWeekDay:int=1;
		public var year:int=-1;
		public var month:int=-1;
		public var enabledHeader:Boolean = true;
		
		public var selectedButton:Day;
		public var todayEvent:Day;
		private var isInitialized:Boolean;
		private var initialDay:int;
      	
		private var _events:ArrayCollection;
		public function set events(value:ArrayCollection):void {
			_events=value;
			invalidateProperties();
		}
		public function get events():ArrayCollection {
			return _events;
		}
		
		private var _tileWidth:int=21;		
		public function set tileWidth(value:int):void {
			_tileWidth=value;
			setDateChooserWidth();
		}
		public function get tileWidth():int {
			return _tileWidth;
		}
		
		private var _tileHeight:int=15;		
		public function set tileHeight(value:int):void {
			_tileHeight=value;
			setDateChooserHeight();
		}
		public function get tileHeight():int {
			return _tileHeight;
		}
		
		public function get selectedDate():Date {
			var date:Date = null;
			if (selectedButton != null) {
				date = new Date();
				date.fullYear = titleYear.value;
				date.month = titleMonth.selectedIndex;
				date.date = selectedButton.number;
				//date = dayDate;//new Date(titleYear.value, titleMonth.selectedIndex, selectedButton.number);
			}
			return date;
		}
		
		private var bg:SkinnableContainer;
		private var headerContent:SkinnableContainer;
		private var weekContent:SkinnableContainer;
		private var centralContent:SkinnableContainer;
		private var titleYear:NumericStepper;
		private var titleMonth:ComboBox;
		
		private var today:Date;
		private var dateChooserWidth:int;
		private var dateChooserHeight:int;
		
		private const HEADER_HEIGHT:int=26;
		private const WEEK_HEIGHT:int=15;
		private const BG_PADDING:int=1;
		private const CENTRAL_PADDING:int=2;
		private const HEADER_PADDING:int=1;
		private const HORIZONTAL_GAP:int=2;
		private const VERTICAL_GAP:int=2;
		private const TOTAL_DAYS:int=42;
		
		public function Calendario() {
			today = new Date();
			if (year < 0) {
				year = today.getFullYear();
			}
			if (month < 0) {
				month = today.getMonth();
			}
			setDateChooserWidth();
			setDateChooserHeight();
			setStyle('backgroundColor','white');
			initialDay = calculateInitialDay();
		}
		
		private function setDateChooserWidth():void {
			dateChooserWidth=(tileWidth*7)+(HORIZONTAL_GAP*6)+(BG_PADDING*2)+(CENTRAL_PADDING*2);
		}
		
		private function setDateChooserHeight():void {
			dateChooserHeight=(tileHeight*6)+(VERTICAL_GAP*5)+HEADER_HEIGHT+WEEK_HEIGHT+
				(BG_PADDING*2)+(CENTRAL_PADDING*2);
		}
		
		override protected function commitProperties():void {
			buildDays();

			// Check events and disabled
			checkEvents();
			
			// Check today
			if (year == today.getFullYear() && month == today.getMonth()) {
				//var todayButton:Day = Day(centralContent.getElementAt(today.date-1+initialDay+firstWeekDay));
				
				//var todayButton:Day;
				if (hasEmptyPreviousDays(initialDay + firstWeekDay)) {
					todayEvent = Day(centralContent.getElementAt(today.date+initialDay-firstWeekDay));
				} else {
					todayEvent = Day(centralContent.getElementAt(today.date-1));
				}
				
				todayEvent.setTodayStyle(); 
				if (!isInitialized) {
					initCalendario(todayEvent);
					isInitialized = true;
				}
			}
			/*
			if (selectedButton != null && isInitialized 
				&& selectedButton.dayData.date.fullYear == titleYear.value 
				&& selectedButton.dayData.date.month == titleMonth.selectedIndex) {
				var selectedDay:Day = Day(centralContent.getElementAt(selectedButton.dayData.date.date-1+initialDay+firstWeekDay));
				selectedDay.selected = true;
				initCalendario(selectedDay);
			}
			*/
			if (selectedButton != null && selectedButton.dayDate != null && isInitialized 
				&& year == titleYear.value 
				&& month == titleMonth.selectedIndex) {
				var selectedDay:Day;
				if (hasEmptyPreviousDays(initialDay + firstWeekDay)) {
					selectedDay = Day(centralContent.getElementAt(selectedButton.dayDate.date-1+initialDay+firstWeekDay-1));
				} else {
					selectedDay = Day(centralContent.getElementAt(selectedButton.dayDate.date-1));
				}
				selectedDay.selected = true;
				initCalendario(selectedDay);
			}
		}
		
		override protected function createChildren():void {
			super.createChildren();
			
			var innerWidth:int=(tileWidth*7)+(HORIZONTAL_GAP*7)+(CENTRAL_PADDING);
			
			// General
			var bgLayout:VerticalLayout = new VerticalLayout ();
			bgLayout.gap = 0;
			bgLayout.paddingBottom = BG_PADDING;
			bgLayout.paddingLeft = BG_PADDING;
			bgLayout.paddingRight = BG_PADDING;
			bgLayout.paddingTop = BG_PADDING;
			
			bg = new SkinnableContainer ();
			bg.styleName = "bgCalendar";
			bg.layout = bgLayout;
			
			// Header
			var headerLayout:HorizontalLayout = new HorizontalLayout();
			headerLayout.gap = 0;
			headerLayout.paddingBottom = HEADER_PADDING;
			headerLayout.paddingLeft = HEADER_PADDING;
			headerLayout.paddingRight = HEADER_PADDING;
			headerLayout.paddingTop = HEADER_PADDING;
			headerLayout.verticalAlign = "top";
			
			headerContent = new SkinnableContainer ();
			headerContent.styleName = "headerContent";
			headerContent.layout = headerLayout;
			headerContent.width=innerWidth;
			headerContent.height=HEADER_HEIGHT;
			
			// Year and Month
			titleYear=new NumericStepper();
			titleYear.enabled = enabledHeader;
			titleYear.addEventListener(NumericStepperEvent.CHANGE, onChangeYear);
			titleYear.maximum=9999;
			titleYear.value=year;
			titleYear.percentWidth=100;
			titleYear.maxWidth=65;
			titleYear.styleName = "titleYear";
			
			titleMonth=new ComboBox();
			titleMonth.enabled = enabledHeader;
			titleMonth.addEventListener(Event.CHANGE, onChangeMonth);
			titleMonth.selectedIndex=month;
			titleMonth.percentWidth=100;
			titleMonth.styleName = "titleMonth";
			titleMonth.setStyle("skinClass", CalendarioComboBoxSkin);
				
			var b1:Button = new Button();
			b1.enabled = enabledHeader;
			b1.width = 22;
			b1.height = 23;
			b1.label = '<';
			b1.addEventListener(MouseEvent.CLICK, function (e:Event):void {
				backMonth();
			})
			
			var b2:Button = new Button();
			b2.enabled = enabledHeader;
			b2.width = 22;
			b2.height = 23;
			b2.label = '>';
			b2.addEventListener(MouseEvent.CLICK, function (e:Event):void {
				forwardMonth();
			})
			
			headerContent.addElement(b1);
			headerContent.addElement(titleMonth);
			headerContent.addElement(titleYear);
			headerContent.addElement(b2);

			// Week
			var weekLayout:HorizontalLayout = new HorizontalLayout();
			weekLayout.gap = 0;
			weekLayout.verticalAlign = "bottom";
			
			weekContent=new SkinnableContainer ();
			weekContent.styleName = "weekContent";
			weekContent.layout = weekLayout;
			weekContent.width=innerWidth;
			weekContent.height=WEEK_HEIGHT;

			for each(var item:String in dayNames) {
    			var it:Label=new Label();
    			it.text=item;
    			it.setStyle('textAlign','center');
    			it.percentWidth=100;
    			weekContent.addElement(it);
  			}

			// Central
			var centralLayout:TileLayout = new TileLayout();
			centralLayout.verticalGap = VERTICAL_GAP;
			centralLayout.horizontalGap = HORIZONTAL_GAP;
			centralLayout.paddingBottom = CENTRAL_PADDING;
			centralLayout.paddingLeft = CENTRAL_PADDING;
			centralLayout.paddingRight = CENTRAL_PADDING;
			centralLayout.paddingTop = CENTRAL_PADDING;

			centralContent = new SkinnableContainer ();
			centralContent.styleName = "centralContent";
			centralContent.layout = centralLayout;
			centralContent.width=innerWidth;
			centralContent.height=(tileHeight*6)+(VERTICAL_GAP*6)+(CENTRAL_PADDING);

			// Add components
			bg.addElement(headerContent);
			bg.addElement(weekContent);
			bg.addElement(centralContent);
			
			addElement(bg);
			
			CalendarioComboBoxSkin(titleMonth.skin).rowCount = rowCount;
		}
		
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
			super.updateDisplayList(unscaledWidth, unscaledHeight);
			// Dimensions are relative 'tileWidth' and 'tileHight'
			titleMonth.dataProvider=new ArrayList(monthNames);
			width=dateChooserWidth;
			height=dateChooserHeight;

			titleYear.value=year;
			titleMonth.selectedIndex=month;
		}
		
		private function buildDays():void {
			// clear previous
			centralContent.removeAllElements();

			var daysMonth:int=calculateDaysMonth();
			var day:Day;
			initialDay = calculateInitialDay();
			// Previous month days
			if (hasEmptyPreviousDays(initialDay + firstWeekDay)) {
				for (var i:int=initialDay+firstWeekDay;i>0;i--) {
					day=new Day();
					day.alpha=0.5;
					day.enabled=false;
					day.width = tileWidth;
					day.height = tileHeight;
					centralContent.addElement(day);
				}
			}
			// Actual month	days
			for (var j:int=1;j<=daysMonth;j++) {
				day=new Day();
				/*
				var event:CalendarioEvent = new CalendarioEvent();
				event.date = new Date(year+'/'+(month+1)+'/'+j);
				day.dayData = event;
				day.addEvent(event);
				*/
				var date:Date = new Date();
				date.fullYear = year;//today.fullYear;
				date.month = month;//today.month;
				date.date = j;
				day.dayDate = date;//(today.fullYear,today.month,j);
				day.number = j;
				day.name = 'day'+j;
				day.label = ''+j;
				day.addEventListener(MouseEvent.CLICK, onDayClick);
				day.width = tileWidth;
				day.height = tileHeight;
				
				// Check if button day is previous today
				if (day.dayDate < today) {
					day.setPreviousTodayStyle();
				}
				
				centralContent.addElement(day);
			}
			// Next month days
			for (var k:int=TOTAL_DAYS-initialDay-firstWeekDay;k>daysMonth;k--) {
				day=new Day();
				day.alpha=0.5;
				day.enabled=false;
				day.width = tileWidth;
				day.height = tileHeight;
				centralContent.addElement(day);
			}
		}
		
		private function onChangeYear(event:Event):void {
			year=NumericStepper(event.target).value;
			var evt:SelectDayEvent = new SelectDayEvent(SelectDayEvent.CHANGE);
			evt.date = new Date(year,month);
			dispatchEvent(evt);

			invalidateProperties();
			invalidateDisplayList();

		}
		
		private function onChangeMonth(event:IndexChangeEvent):void {
			month=event.target.selectedIndex;
			changeMonth();
		}
		
		private function changeMonth():void {
			invalidateProperties();
			invalidateDisplayList();
			
			var evt:SelectDayEvent = new SelectDayEvent(SelectDayEvent.CHANGE);
			evt.date = new Date(year,month);
			dispatchEvent(evt);
		}
		
		private function calculateInitialDay():int {
			var myDate:Date=new Date(year,month,0);
			return myDate.getDay();
		}
		
		private function calculateDaysMonth():int {
			var days:int;
			switch (month) {
				case 0:
					days=31; // January
					break;
				case 1:
					days=isLeap(); // February
					break;
				case 2:
					days=31; // March
					break;
				case 4:
					days=31; // May
					break;
				case 6:
					days=31; // July
					break;
				case 7:
					days=31; // August
					break;
				case 9:
					days=31; // October
					break;
				case 11:
					days=31; // December
					break;
				default:
					days=30; // 30 days months
					break;
			}
			return days;
		}
		
		private function isLeap():int {
			if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
	            return 29;
	        } else {
	            return 28;
	        }
		}
		
		private function checkEvents():void {
			for each (var item:CalendarioEvent in events) { 
			    if (item.date.getMonth()==month && 
			    	item.date.getFullYear()==year) {
					var selectedDay:Day;
					var day:Day;
					if (hasEmptyPreviousDays(initialDay + firstWeekDay)) {
						day = Day(centralContent.getElementAt(item.date.date-1+initialDay+firstWeekDay-1));
					} else {
						day = Day(centralContent.getElementAt(item.date.date-1));
					}
					
					//var day:Day=Day(centralContent.getElementAt(item.date.date-1+initialDay+firstWeekDay));
					if (item.enabled) {
						//day.setEvent(item);
						day.addEvent(item);
		    		} else {
		    			day.enabled=false;
		    		}	
			    }
			}
		}
		
		private function onDayClick(event:MouseEvent):void {
			var day:Day=Day(event.target);
			var evt:SelectDayEvent;
			
			if (selectedButton != null) {
				if (selectedButton == day) {
					evt = new SelectDayEvent(SelectDayEvent.DESELECT);
					//evt.data = day.dayData;
					evt.events = day.events;
					dispatchEvent(evt);
					selectedButton = null;
				} else {
					selectedButton.selected = false;
					selectedButton = day;
					evt = new SelectDayEvent(SelectDayEvent.SELECT);
					//evt.data = day.dayData;
					evt.events = day.events;
					dispatchEvent(evt);
				}
			} else {
				selectedButton = day;
				evt=new SelectDayEvent(SelectDayEvent.SELECT);
				//evt.data = day.dayData;
				evt.events = day.events;
				dispatchEvent(evt);
			}
		}
		
		private function initCalendario (day:Day):void {
			selectedButton = day;
			var evt:SelectDayEvent;
			evt=new SelectDayEvent(SelectDayEvent.SELECT);
			//evt.data = day.dayData;
			evt.events = day.events;
			dispatchEvent(evt);
		}
		
		private function hasEmptyPreviousDays(day:int):Boolean {
			var result:Boolean = false;
			//if (initialDay + firstWeekDay != WEEK) {
			if (day != 0) {
				result = true;
			}
			return result;
		}
		
		public function forwardMonth():void {
			if (month == 11) {
				month = 0;
				year ++;
			} else {
				month ++;
			}
			
			changeMonth();
		}
		
		public function backMonth():void {
			if (month == 0) {
				month == 11;
				year --;
			} else {
				month --;
			}
			
			changeMonth();
		}
	}
}